package org.lex.game.sudoku.view;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import ms.sudoku.util.IntSet;

import org.lex.game.sudoku.SudokuApplication;
import org.lex.game.sudoku.datatype.HintType;
import org.lex.game.sudoku.datatype.SelectType;
import org.lex.game.sudoku.model.Point;
import org.lex.game.sudoku.model.SudokuModel;
import org.lex.game.sudoku.model.SudokuModelListener;

import common.Sudokus;

public class HistoryBoardController extends AbstractController<HistoryBoard> implements ListSelectionListener,
		SudokuModelListener {
	static final String MARK_ACTION = "historyBoard.mark";
	static final String ROLLBACK_ACTION = "historyBoard.rollback";

	private final SudokuModel sudokuModel;

	public HistoryBoardController(SudokuApplication app, HistoryBoard view, SudokuModel sudokuModel) {
		super(app, view);
		this.sudokuModel = sudokuModel;
		this.sudokuModel.addListener(this);
		// initialize
		ActionMap am = this.getActionMap();
		am.put(MARK_ACTION, new MarkAction());
		am.put(ROLLBACK_ACTION, new RollbackAction());
	}

	void selectedCellChanged(SelectType type, int row, int column) {
		CellHintPane hintPane = this.getView().getHintPane();
		if (HintType.NONE != this.getApplication().getApplicationView().getSudokuBoard().getHintType()
				&& Sudokus.isEmpty(this.sudokuModel.get(row, column))) {
			IntSet hints = sudokuModel.getHints(row, column);
			hintPane.setHints(sudokuModel.getGridSize(), sudokuModel.getBoxSize(), hints);
		} else {
			hintPane.clearHints();
		}
	}

	// =====ListSelectionListener: history table's selection listener=====
	@Override
	public void valueChanged(ListSelectionEvent e) {
		if (false == e.getValueIsAdjusting()) {
			Action rollbackAction = this.getActionMap().get(HistoryBoardController.ROLLBACK_ACTION);
			Action markAction = this.getActionMap().get(HistoryBoardController.MARK_ACTION);

			HistoryBoard view = this.getView();
			JTable table = view.getTable();
			int selectedRowCount = table.getSelectedRowCount();
			if (1 == selectedRowCount) {
				rollbackAction.setEnabled(true);
				markAction.setEnabled(!view.getTableModel().isMarked(table.getSelectedRow()));
			} else {
				rollbackAction.setEnabled(false);
				markAction.setEnabled(false);
			}
		}
	}

	// =====SudokuModelListener: listen for SudokuModel's change events=====

	@Override
	public void cellChanged(SudokuModel model, Cause cause, int row, int col, int oldValue, int newValue) {
		if (Cause.USER == cause) {
			HistoryTableModel tableModel = this.getView().getTableModel();
			tableModel.addOperation(new Operation(System.currentTimeMillis(), new Point(row, col), oldValue,
					newValue));
		}
	}

	@Override
	public void modelChanged(SudokuModel model) {
		this.getView().getTableModel().reset();
	}

	private void mark() {
		HistoryBoard view = this.getView();
		JTable table = view.getTable();
		if (1 == table.getSelectedRowCount()) {
			view.getTableModel().addMarker(table.getSelectedRow());
		}
	}

	private void rollback() {
		HistoryBoard view = this.getView();
		JTable table = view.getTable();
		if (1 == table.getSelectedRowCount()) {
			HistoryTableModel historyTableModel = view.getTableModel();
			int selectedRow = table.getSelectedRow();
			int lastRow = historyTableModel.getRowCount();
			log.debug("preparing to rollback [" + selectedRow + ", " + lastRow + "]");
			int ret = JOptionPane.showConfirmDialog(getMainFrame(), "Are you sure to rollback last "
					+ (lastRow - selectedRow) + " operations?", "Confirm", JOptionPane.YES_NO_OPTION,
					JOptionPane.QUESTION_MESSAGE);
			if (JOptionPane.YES_OPTION == ret) {
				List<Operation> rollbacks = historyTableModel.getHistoryOperations(selectedRow, lastRow);
				historyTableModel.rollback(rollbacks);
				this.getApplication().getApplicationView().getSudokuBoard().rollback(rollbacks);
			}
		}
	}

	private class MarkAction extends AbstractAction {
		private static final long serialVersionUID = 5748727798826853594L;

		public MarkAction() {
			super("Mark");
			this.putValue(MNEMONIC_KEY, KeyEvent.VK_M);
			this.setEnabled(false);
		}

		public void actionPerformed(ActionEvent e) {
			mark();
		}
	}

	private class RollbackAction extends AbstractAction {
		private static final long serialVersionUID = 5748727798826853594L;

		public RollbackAction() {
			super("Rollback");
			this.putValue(MNEMONIC_KEY, KeyEvent.VK_R);
			this.setEnabled(false);
		}

		public void actionPerformed(ActionEvent e) {
			rollback();
		}
	}
}
